package x

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"log"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"
	"unicode/utf8"
)

func parseTagSetting(tags reflect.StructTag) map[string]string {
	setting := map[string]string{}
	str := tags.Get("valid")
	if str != "" {
		tags := strings.Split(str, ";")
		for _, value := range tags {
			v := strings.Split(value, ":")
			k := strings.TrimSpace(strings.ToLower(v[0]))
			if len(v) >= 2 {
				setting[k] = strings.TrimSpace(strings.Join(v[1:], ":"))
			} else {
				setting[k] = k
			}
		}
	}
	return setting
}
func parseTagName(tags reflect.StructTag, defaultString string) string {
	if c := tags.Get("comment"); strings.TrimSpace(c) != "" {
		return strings.TrimSpace(c)
	}
	if j := tags.Get("json"); strings.TrimSpace(j) != "" {
		j = strings.TrimSpace(j)
		if j != "-" {
			return strings.TrimSpace(strings.Split(j, ",")[0])
		}
	}
	return defaultString
}

type structField struct {
	Name        string
	Value       reflect.Value
	TagSettings map[string]string
}

func (sf *structField) getTag(key string) (string, bool) {
	val, ok := sf.TagSettings[key]
	return val, ok
}
func validStructField(x *structField) error {
	if _, ok := x.getTag("require"); ok {
		if isEmpty(x.Value) {
			return fmt.Errorf("%s不可为空", x.Name)
		}
	}
	if v, ok := x.getTag("gt"); ok {
		i, e := compare(x.Value, v)
		if e != nil {
			return e
		}
		if i <= 0 {
			return fmt.Errorf("%s必需大于%s", x.Name, v)
		}
	}
	if v, ok := x.getTag("ge"); ok {
		i, e := compare(x.Value, v)
		if e != nil {
			return e
		}
		if i < 0 {
			return fmt.Errorf("%s不能小于%s", x.Name, v)
		}
	}
	if v, ok := x.getTag("lt"); ok {
		i, e := compare(x.Value, v)
		if e != nil {
			return e
		}
		if i >= 0 {
			return fmt.Errorf("%s必需小于%s", x.Name, v)
		}
	}
	if v, ok := x.getTag("le"); ok {
		i, e := compare(x.Value, v)
		if e != nil {
			return e
		}
		if i > 0 {
			return fmt.Errorf("%s不能大于%s", x.Name, v)
		}
	}
	if v, ok := x.getTag("max"); ok {
		l, e := strconv.ParseInt(v, 10, 64)
		if e != nil {
			return fmt.Errorf("value must kind of number")
		}
		if int64(utf8.RuneCountInString(fmt.Sprintf("%v", x.Value))) > l {
			return fmt.Errorf("%s字符数量不能超过%s", x.Name, v)
		}
	}
	if v, ok := x.getTag("min"); ok {
		l, e := strconv.ParseInt(v, 10, 64)
		log.Println(v)
		if e != nil {
			return fmt.Errorf("value must kind of number")
		}
		if int64(utf8.RuneCountInString(fmt.Sprintf("%v", x.Value))) < l {
			return fmt.Errorf("%s字符数量不能少于%s", x.Name, v)
		}
	}
	if v, ok := x.getTag("reg"); ok {
		y, e := regexp.MatchString(v, fmt.Sprintf("%v", x.Value))
		if e != nil {
			return fmt.Errorf("err regexp")
		}
		if !y {
			return fmt.Errorf("%s格式非法", x.Name)
		}
	}
	if _, ok := x.getTag("mobile"); ok {
		y, e := regexp.MatchString("^1\\d{10}", fmt.Sprintf("%v", x.Value))
		if e != nil {
			return fmt.Errorf("err regexp")
		}
		if !y {
			return fmt.Errorf("%s不是正确的手机号", x.Name)
		}
	}
	if _, ok := x.getTag("identity"); ok {
		value := fmt.Sprintf("%v", x.Value)
		y, e := regexp.MatchString(`^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$`, value)
		if e != nil {
			return fmt.Errorf("err regexp")
		}
		if !y {
			return fmt.Errorf("%s不是正确的身份证号", x.Name)
		}
		factory := []int{7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}
		sum := 0
		for k, v := range factory {
			c, _ := strconv.Atoi(value[k : k+1])
			sum = sum + c*v
		}
		parity := []string{"1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"}
		c := parity[sum%11]
		end := value[17:]
		log.Println(c)
		if c != end {
			return fmt.Errorf("%s不是正确的身份证号", x.Name)
		}
		//验证日期
		_, err := time.Parse("20060102", value[6:14])
		if err != nil {
			return fmt.Errorf("%s不是正确的身份证号", x.Name)
		}
	}
	return nil
}
func validField(x reflect.StructField, y reflect.Value) error {
	tags := parseTagSetting(x.Tag)
	name := parseTagName(x.Tag, x.Name)
	sf := &structField{Name: name, TagSettings: tags, Value: y}
	return validStructField(sf)
}
func Valid(x interface{}) error {
	kind := reflect.TypeOf(x)
	value := reflect.ValueOf(x)
	if kind.Kind() == reflect.Ptr {
		kind = kind.Elem()
		value = value.Elem()
	}
	if kind.Kind() != reflect.Struct {
		return fmt.Errorf("valid target must be struct")
	}
	for i := 0; i < kind.NumField(); i++ {
		field := kind.Field(i)
		val := value.Field(i)
		if _, ok := field.Tag.Lookup("valid"); ok {
			if e := validField(field, val); e != nil {
				return e
			}
		}
	}
	return nil
}
func Encrypt(data string, key string) string {
	hash := fmt.Sprintf("%s:%s", data, key)
	h := md5.New()
	h.Write([]byte(hash))
	return hex.EncodeToString(h.Sum(nil))
}
